/**
 * AnnotationArrow.java Created on : 28 juin 07
 */
package fr.limsi.seven;

import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;

import javax.swing.ImageIcon;
import javax.swing.JLabel;

/**
 * @author Camille
 */
public class AnnotationArrow extends Annotation {

	/**
	 * Auto-generated serial version UID number
	 */
	private static final long serialVersionUID = 3111685819471476947L;

	/**
	 * Used icon size (rotation button)
	 */
	public static final int ICON_SIZE = 22;

	/**
	 * An arrow direction integer constant
	 */
	public static final int UP = 0;

	/**
	 * An arrow direction integer constant
	 */
	public static final int RIGHT = 1;

	/**
	 * An arrow direction integer constant
	 */
	public static final int DOWN = 2;

	/**
	 * An arrow direction integer constant
	 */
	public static final int LEFT = 3;

	/**
	 * Count of all direction integer constant (current count = 4)
	 */
	public static final int DIRECTIONS_COUNT = 4;

	/**
	 * Current arrow direction indicator
	 */
	protected int direction;

	/**
	 * An arrow control point (rectangle limit)
	 */
	protected ControlPoint rLeft;

	/**
	 * An arrow control point (rectangle limit)
	 */
	protected ControlPoint rRight;

	/**
	 * An arrow control point (triangle base left point)
	 */
	protected ControlPoint tLeft;

	/**
	 * An arrow control point (triangle base right point)
	 */
	protected ControlPoint tRight;

	/**
	 * An arrow control point (triangle peak)
	 */
	protected ControlPoint tPeak;

	protected JLabel rotateButton;

	/**
	 * Default constructor
	 * 
	 * @param pyramide2D
	 *            the associated {@link Pyramide2D}
	 */
	public AnnotationArrow(Pyramide2D pyramide2D) {
		super(pyramide2D);

		direction = LEFT;
		rotateButton = new JLabel(new ImageIcon(getClass().getResource("/fr/limsi/seven/images/rotate.png")));

		rLeft = new ControlPoint(this, ControlPoint.VERTICAL);
		rRight = new ControlPoint(this, ControlPoint.VERTICAL);
		tLeft = new ControlPoint(this, ControlPoint.HORIZONTAL);
		tRight = new ControlPoint(this, ControlPoint.HORIZONTAL);
		tPeak = new ControlPoint(this, ControlPoint.VERTICAL);

		rLeft.setRelativePosition(1.0f, (1.0f / 4.0f));
		rRight.setRelativePosition(1.0f, (2.5f / 4.0f));
		tLeft.setRelativePosition((2.0f / 5.0f), 0);
		tRight.setRelativePosition((2.0f / 5.0f), 1.0f);
		tPeak.setRelativePosition(0, 0.5f);

		rLeft.addAvoider(rRight);
		rRight.addAvoider(rLeft);

		tLeft.addFollower(tRight);
		tRight.addFollower(tLeft);

		addControlPoint(rLeft);
		addControlPoint(rRight);
		addControlPoint(tLeft);
		addControlPoint(tRight);
		addControlPoint(tPeak);

		buildAdditionnalInteractions();
	}

	/**
	 * Creates and sets specific interactions
	 */
	protected void buildAdditionnalInteractions() {

		MouseAdapter rotateClick = new MouseAdapter() {

			@Override
			public void mouseEntered(MouseEvent e) {
				setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
			}

			@Override
			public void mouseExited(MouseEvent e) {
				setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			}

			@Override
			public void mouseClicked(MouseEvent e) {
				setDirection((direction + 1) % DIRECTIONS_COUNT);
			}
		};

		rotateButton.addMouseListener(rotateClick);

		FocusAdapter fA = new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent e) {
				pyramide2D.add(rotateButton, 0);
				rotateButton.setVisible(true);
				rotateButton.setBounds(getX() + (getWidth() / 2), getY() + getHeight() + ControlPoint.DEFAULT_SIZE, ICON_SIZE, ICON_SIZE);

				repaint();
			}

			@Override
			public void focusLost(FocusEvent e) {
				pyramide2D.remove(rotateButton);

				repaint();
			}
		};

		addFocusListener(fA);
	}

	/**
	 * Getter for the arrow direction
	 * 
	 * @return the current corresponding direction constant integer
	 */
	public int getDirection() {
		return direction;
	}

	/**
	 * Setter for the arrow direction
	 * 
	 * @param direction
	 *            the new direction to be set
	 */
	public void setDirection(int direction) {
		while (this.direction != direction) {
			changeDirection((this.direction + 1) % DIRECTIONS_COUNT);
		}
	}

	/**
	 * Changes the arrow direction (also updates the {@link ControlPoint} positions)
	 * 
	 * @param newDirection
	 *            the new direction constant integer to be set
	 */
	protected void changeDirection(int newDirection) {
		switch (newDirection) {

		case UP:

			rLeft.setRelativePosition(rLeft.getRelativeYPosition(), 1.0f);
			rRight.setRelativePosition(rRight.getRelativeYPosition(), 1.0f);
			tLeft.setRelativePosition(0, tLeft.getRelativeXPosition());
			tRight.setRelativePosition(1.0f, tRight.getRelativeXPosition());
			tPeak.setRelativePosition(tPeak.getRelativeYPosition(), 0);

			rLeft.setType(ControlPoint.HORIZONTAL);
			rRight.setType(ControlPoint.HORIZONTAL);
			tLeft.setType(ControlPoint.VERTICAL);
			tRight.setType(ControlPoint.VERTICAL);
			tPeak.setType(ControlPoint.HORIZONTAL);
			break;

		case RIGHT:

			rLeft.setRelativePosition(0, rLeft.getRelativeXPosition());
			rRight.setRelativePosition(0, rRight.getRelativeXPosition());
			tLeft.setRelativePosition(tLeft.getRelativeYPosition(), 0);
			tRight.setRelativePosition(tRight.getRelativeYPosition(), 1.0f);
			tPeak.setRelativePosition(1.0f, tPeak.getRelativeXPosition());

			rLeft.setType(ControlPoint.VERTICAL);
			rRight.setType(ControlPoint.VERTICAL);
			tLeft.setType(ControlPoint.HORIZONTAL);
			tRight.setType(ControlPoint.HORIZONTAL);
			tPeak.setType(ControlPoint.VERTICAL);
			break;

		case DOWN:

			rLeft.setRelativePosition(rLeft.getRelativeYPosition(), 0);
			rRight.setRelativePosition(rRight.getRelativeYPosition(), 0);
			tLeft.setRelativePosition(1.0f, tLeft.getRelativeXPosition());
			tRight.setRelativePosition(0, tRight.getRelativeXPosition());
			tPeak.setRelativePosition(tPeak.getRelativeYPosition(), 1.0f);

			rLeft.setType(ControlPoint.HORIZONTAL);
			rRight.setType(ControlPoint.HORIZONTAL);
			tLeft.setType(ControlPoint.VERTICAL);
			tRight.setType(ControlPoint.VERTICAL);
			tPeak.setType(ControlPoint.HORIZONTAL);
			break;

		case LEFT:

			rLeft.setRelativePosition(1.0f, rLeft.getRelativeXPosition());
			rRight.setRelativePosition(1.0f, rRight.getRelativeXPosition());
			tLeft.setRelativePosition(tRight.getRelativeYPosition(), 0);
			tRight.setRelativePosition(tRight.getRelativeYPosition(), 1.0f);
			tPeak.setRelativePosition(0, tPeak.getRelativeXPosition());

			rLeft.setType(ControlPoint.VERTICAL);
			rRight.setType(ControlPoint.VERTICAL);
			tLeft.setType(ControlPoint.HORIZONTAL);
			tRight.setType(ControlPoint.HORIZONTAL);
			tPeak.setType(ControlPoint.VERTICAL);
			break;

		}

		direction = newDirection;

		update();
		repaint();
	}

	@Override
	public void draw(Graphics2D g2) {

		GeneralPath rectangle = new GeneralPath();
		rectangle.moveTo((rLeft.getRelativeXPosition() * getWidth()) + (rLeft.getWidth() / 2), (rLeft.getRelativeYPosition() * getHeight()) + (rLeft.getHeight() / 2));

		if (direction == UP || direction == DOWN) {
			rectangle.lineTo((rLeft.getLocationOnScreen().x - getLocationOnScreen().x) + (rLeft.getWidth() / 2), (tLeft.getLocationOnScreen().y - getLocationOnScreen().y) + (tLeft.getHeight() / 2.0f));
			rectangle.lineTo((rRight.getLocationOnScreen().x - getLocationOnScreen().x) + (rRight.getWidth() / 2), (tLeft.getLocationOnScreen().y - getLocationOnScreen().y) + (tLeft.getHeight() / 2.0f));
		}
		else { // LEFT or RIGHT
			rectangle.lineTo((tLeft.getLocationOnScreen().x - getLocationOnScreen().x) + (tLeft.getWidth() / 2), (rLeft.getLocationOnScreen().y - getLocationOnScreen().y) + (rLeft.getHeight() / 2));
			rectangle.lineTo((tLeft.getLocationOnScreen().x - getLocationOnScreen().x) + (tLeft.getWidth() / 2), (rRight.getLocationOnScreen().y - getLocationOnScreen().y) + (rRight.getHeight() / 2));
		}

		rectangle.lineTo((rRight.getRelativeXPosition() * getWidth()) + (rRight.getWidth() / 2), (rRight.getRelativeYPosition() * getHeight()) + (rRight.getHeight() / 2));
		rectangle.lineTo((rLeft.getRelativeXPosition() * getWidth()) + (rLeft.getWidth() / 2), (rLeft.getRelativeYPosition() * getHeight()) + (rLeft.getHeight() / 2));
		rectangle.closePath();

		GeneralPath triangle = new GeneralPath();
		triangle.moveTo((tLeft.getLocationOnScreen().x - getLocationOnScreen().x) + (tLeft.getWidth() / 2.0f), (tLeft.getLocationOnScreen().y - getLocationOnScreen().y) + (tLeft.getHeight() / 2.0f));
		triangle.lineTo((tPeak.getLocationOnScreen().x - getLocationOnScreen().x) + (tPeak.getWidth() / 2.0f), (tPeak.getLocationOnScreen().y - getLocationOnScreen().y) + (tPeak.getHeight() / 2.0f));
		triangle.lineTo((tRight.getLocationOnScreen().x - getLocationOnScreen().x) + (tRight.getWidth() / 2.0f), (tRight.getLocationOnScreen().y - getLocationOnScreen().y) + (tRight.getHeight() / 2.0f));
		triangle.lineTo((tLeft.getLocationOnScreen().x - getLocationOnScreen().x) + (tLeft.getWidth() / 2.0f), (tLeft.getLocationOnScreen().y - getLocationOnScreen().y) + (tLeft.getHeight() / 2.0f));
		triangle.closePath();

		Area arrow = new Area();
		arrow.add(new Area(rectangle));
		arrow.add(new Area(triangle));

		if (rotateButton.isShowing()) {
			rotateButton.setBounds(getX() + (getWidth() / 2) - (ICON_SIZE / 2), getY() + getHeight() + ControlPoint.DEFAULT_SIZE, ICON_SIZE, ICON_SIZE);
		}

		g2.setPaint(mainColor);
		g2.fill(arrow);
	}

	@Override
	public void correctControlPointPositions() {
		switch (direction) {

		case UP:

			rLeft.setLocation(rLeft.getX(), rLeft.getY() - (rLeft.getHeight() / 2));
			rRight.setLocation(rRight.getX(), rRight.getY() - (rRight.getHeight() / 2));

			tLeft.setLocation(tLeft.getX() - (tLeft.getWidth() / 2), tLeft.getY());
			tRight.setLocation(tRight.getX() - (tRight.getWidth() / 2), tRight.getY());
			tPeak.setLocation(tPeak.getX(), tPeak.getY() - (tPeak.getHeight() / 2));

			break;

		case RIGHT:

			rLeft.setLocation(rLeft.getX() - (rLeft.getWidth() / 2), rLeft.getY());
			rRight.setLocation(rRight.getX() - (rRight.getWidth() / 2), rRight.getY());

			tLeft.setLocation(tLeft.getX(), tLeft.getY() - (tLeft.getHeight() / 2));
			tRight.setLocation(tRight.getX(), tRight.getY() - (tRight.getHeight() / 2));
			tPeak.setLocation(tPeak.getX() - (tPeak.getWidth() / 2), tPeak.getY());

			break;

		case DOWN:

			rLeft.setLocation(rLeft.getX(), rLeft.getY() - (rLeft.getHeight() / 2));
			rRight.setLocation(rRight.getX(), rRight.getY() - (rRight.getHeight() / 2));

			tLeft.setLocation(tLeft.getX() - (tLeft.getWidth() / 2), tLeft.getY());
			tRight.setLocation(tRight.getX() - (tRight.getWidth() / 2), tRight.getY());
			tPeak.setLocation(tPeak.getX(), tPeak.getY() - (tPeak.getHeight() / 2));

			break;

		case LEFT:

			rLeft.setLocation(rLeft.getX() - (rLeft.getWidth() / 2), rLeft.getY());
			rRight.setLocation(rRight.getX() - (rRight.getWidth() / 2), rRight.getY());

			tLeft.setLocation(tLeft.getX(), tLeft.getY() - (tLeft.getHeight() / 2));
			tRight.setLocation(tRight.getX(), tRight.getY() - (tRight.getHeight() / 2));
			tPeak.setLocation(tPeak.getX() - (tPeak.getWidth() / 2), tPeak.getY());

			break;
		}
	}

	@Override
	public int getAnnotationType() {
		return TYPE_ARROW;
	}

	@Override
	public String getContent() {
		if (getToolTipText() != null && !getToolTipText().equals("")) {
			return getToolTipText();
		}
		else {
			return "Pas de contenu";
		}
	}

	@Override
	public void setContent(String content) {
		setToolTipText(content);
	}

	@Override
	public int getDefaultWidth() {
		return 80;
	}

	@Override
	public int getDefaultHeight() {
		return 40;
	}
}
